package org.lboot.flow.processor.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Validator;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.lboot.flow.constant.FlowNodeTypeConst;
import org.lboot.flow.constant.FlowWorkStatusConst;
import org.lboot.flow.loader.DecisionMarkerLoader;
import org.lboot.flow.model.FlowModel;
import org.lboot.flow.model.node.BaseModel;
import org.lboot.flow.model.node.ExclusiveGatewayModel;
import org.lboot.flow.module.process.FlowProcessService;
import org.lboot.flow.module.work.FlowWork;
import org.lboot.flow.module.work.FlowWorkService;
import org.lboot.flow.module.work.params.FlowTaskParams;
import org.lboot.flow.parser.FlowNodeParser;
import org.lboot.flow.processor.FlowNodeProcessor;
import org.lboot.flow.event.system.FlowBlockEvent;
import org.lboot.flow.event.system.FlowProcessEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author kindear
 * 排他网关解析节点
 */
@Slf4j
@Service
@AllArgsConstructor
public class ExclusiveGatewayNodeProcessorImpl implements FlowNodeProcessor {
    // 流程服务
    FlowWorkService flowWorkService;

    FlowNodeParser flowNodeParser;

    // 流程流转服务
    FlowProcessService flowProcessService;

    // 决策器
    List<DecisionMarkerLoader> decisionMarkers;

    @Resource
    ApplicationContext context;

    @Override
    public String getNodeType() {
        return FlowNodeTypeConst.EXCLUSIVE_GATEWAY_NODE;
    }

    @Override
    @SneakyThrows
    public void process(FlowWork flowWork, FlowTaskParams taskParams) {
        FlowWork oldFlow = new FlowWork();
        BeanUtil.copyProperties(flowWork,oldFlow, CopyOptions.create().setIgnoreError(true).setIgnoreNullValue(true));
        // 流程执行到下一步
        BaseModel currentNode = flowWork.getNodeModel(flowWork.getCurrentNodeId());
        // 排他网关节点
        ExclusiveGatewayModel gatewayModel = (ExclusiveGatewayModel) currentNode;
        // 获取网关可选择节点
        List<String > nodeIds = gatewayModel.getNodeIds();
        // 网关决策器
        String maker = gatewayModel.getDecisionMaker();
        // 服务发现?
        boolean serviceMatch = false;
        // 决策结果
        String decisionResult = null;
        for (DecisionMarkerLoader decisionMarker:decisionMarkers){
            // 存在匹配服务决策
            if (decisionMarker.getMaker().equals(maker)){
                serviceMatch = true;
               // decisionResult = decisionMarker.make(flowWork,gatewayModel,nodeIds);
                break;
            }
        }
        // 不存在决策器 -> 阻塞流程
        if (!serviceMatch){
            log.warn("流程{}:节点{}对应的流程决策器不存在",flowWork.getFlowName(), gatewayModel.getId());
            // 流程置入阻塞
            flowWork.setFlowWorkStatus(FlowWorkStatusConst.BLOCKED);
            flowWorkService.updateOne(flowWork);
            // 流程阻塞事件
            taskParams.setRemark("未找到流程节点["+gatewayModel.getId()+"]的决策器");
            context.publishEvent(new FlowBlockEvent(this,flowWork,taskParams));
            return;
        }
        // 决策结果不在列表中 / null -> 阻塞
        if (Validator.isEmpty(decisionResult)){
            // 已阻塞状态 不进行任何操作
            if (flowWork.getFlowWorkStatus().equals(FlowWorkStatusConst.BLOCKED)){
                return;
            }
            // 流程置入阻塞
            flowWork.setFlowWorkStatus(FlowWorkStatusConst.BLOCKED);
            flowWorkService.updateOne(flowWork);
            // 流程阻塞事件
            taskParams.setRemark("流程节点["+gatewayModel.getId()+"]决策结果不在可选列表中");
            context.publishEvent(new FlowBlockEvent(this,flowWork,taskParams));
            return;
        }

        // 决策执行正常
        BaseModel nextModel = flowWork.getNodeModel(decisionResult);
        flowWork.setCurrentNodeId(nextModel.getId());
        flowWork.setCurrentNodeType(nextModel.getNodeType());
        flowWork.setNextNodeId(nextModel.getNextNodeId());
        flowWorkService.updateOne(flowWork);
        // 发布流程流转事件
        context.publishEvent(new FlowProcessEvent(this,oldFlow,flowWork,taskParams));
    }

    @Override
    public void back(FlowWork flowWork, FlowTaskParams taskParams) {
        log.info("该节点无法执行back");
    }
}
